from os import abort
from sqlalchemy import or_, func
from datetime import datetime, timedelta
from flask import Blueprint, render_template, redirect, url_for, request, flash,current_app, jsonify
from flask_login import login_user, logout_user, login_required
from werkzeug.security import generate_password_hash, check_password_hash
from app.forms import LoginForm, CategoryForm, ProductForm
from app.models import AdminUser, Category, Product, ProductImage, Banner, CategoryI18n, ProductI18n, CustomerUser, Order, \
    BuyClick
from app.utils.utils import save_image
from app.extensions import db
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from app.utils.orders_utils import require_admin
from app.models import Order, Shipment

bp = Blueprint('admin', __name__)


def _to_minor(val):
    return int(Decimal(val) * 100) if val is not None else None


@bp.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        u = AdminUser.query.filter_by(username=form.username_or_email.data).first()
        if u and check_password_hash(u.password_hash, form.password.data):
            login_user(u)
            return redirect(request.form.get('next') or url_for('admin.dashboard'))
        flash('用户名或密码错误', 'danger')
    else:
        if request.method == 'POST':
            current_app.logger.warning('admin.login validate failed: %s', form.errors)
    return render_template('auth/login.html', form=form,
                           post_url=url_for('admin.login'),
                           title='后台登录')

@bp.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('admin.login'))


@bp.route('/')
@login_required
def dashboard():
    prod_count = Product.query.count()
    cat_count = Category.query.count()
    return render_template('admin/dashboard.html', prod_count=prod_count, cat_count=cat_count)


# 分类管理
@bp.route('/categories')
@login_required
def category_list():
    items = Category.query.order_by(Category.sort_order).all()
    return render_template('admin/category_list.html', items=items)


@bp.route('/categories/new', methods=['GET', 'POST'])
@login_required
def category_new():
    form = CategoryForm()
    if form.validate_on_submit():
        c = Category(name=form.name.data, sort_order=form.sort_order.data)
    c.set_slug()
    db.session.add(c)
    db.session.flush()
    # 英文翻译
    if form.name_en.data:
        db.session.add(CategoryI18n(category_id=c.id, lang='en', name=form.name_en.data))
    db.session.commit()
    return redirect(url_for('admin.category_list'))
    return render_template('admin/category_form.html', form=form)


@bp.route('/categories/<int:cid>/edit', methods=['GET', 'POST'])
@login_required
def category_edit(cid):
    c = Category.query.get_or_404(cid)
    form = CategoryForm(obj=c)
    # 取英文翻译用于回显
    i18n_en = CategoryI18n.query.filter_by(category_id=c.id, lang='en').first()
    if request.method == 'GET' and i18n_en:
        form.name_en.data = i18n_en.name
    if form.validate_on_submit():
        c.name = form.name.data
        c.sort_order = form.sort_order.data
        c.set_slug()
        # 保存英文
        if form.name_en.data:
            if i18n_en:
                i18n_en.name = form.name_en.data
            else:
                db.session.add(CategoryI18n(category_id=c.id, lang='en', name=form.name_en.data))
        else:
            if i18n_en:
                db.session.delete(i18n_en)
        db.session.commit()
        return redirect(url_for('admin.category_list'))
    return render_template('admin/category_form.html', form=form)


@bp.route('/categories/<int:cid>/delete', methods=['POST'])
@login_required
def category_delete(cid):
    c = Category.query.get_or_404(cid)
    db.session.delete(c)
    db.session.commit()
    return redirect(url_for('admin.category_list'))


@bp.route('/products')
@login_required
def product_list():
    q = Product.query.order_by(Product.created_at.desc()).all()
    return render_template('admin/product_list.html', items=q)


@bp.route('/products/new', methods=['GET', 'POST'])
@login_required
def product_new():
    form = ProductForm()
    if request.method == 'POST':
        current_app.logger.info("POST /admin/products/new data=%s files=%s",
                                dict(request.form), dict(request.files))
    form.category_id.choices = [(c.id, c.name) for c in Category.query.order_by(Category.sort_order)]
    if form.validate_on_submit():
        price_minor = None
        if form.price_amount.data is not None:
            try:
                price_minor = int(Decimal(form.price_amount.data) * 100)
            except (InvalidOperation, ValueError):
                price_minor = None
        try:
            p = Product(
                title=form.title.data,
                subtitle=form.subtitle.data,
                description=form.description.data,
                price=price_minor,
                currency=form.currency.data,
                sku=form.sku.data,
                material=form.material.data,
                size_value=float(form.size_value.data) if form.size_value.data is not None else None,
                size_length=float(form.size_length.data) if form.size_length.data is not None else None,
                size_width=float(form.size_width.data) if form.size_width.data is not None else None,
                size_height=float(form.size_height.data) if form.size_height.data is not None else None,
                size_unit=form.size_unit.data,
                weight_value=float(form.weight_value.data) if form.weight_value.data is not None else None,
                weight_unit=form.weight_unit.data,
                stock=form.stock.data or 1,  # 新增
                min_order_qty = form.min_order_qty.data or 1,
                has_certificate=(form.has_certificate.data == '1'),
                can_direct_pay=(form.can_direct_pay.data == '1'),
                category_id=form.category_id.data,
                is_published=form.is_published.data,
            )
            if form.cover_image.data:
                p.cover_image = save_image(form.cover_image.data, 'products')
            db.session.add(p)
            db.session.flush()
            # 保存英文翻译（有填才存）
            if any([form.title_en.data, form.subtitle_en.data, form.description_en.data]):
                db.session.add(ProductI18n(
                    product_id=p.id, lang='en',
                    title=form.title_en.data or None,
                    subtitle=form.subtitle_en.data or None,
                    description=form.description_en.data or None,
                    material=form.material_en.data or None
                ))

            gallery_files = request.files.getlist('gallery')

            for fs in gallery_files:
                if not fs or not fs.filename:
                    continue

                try:
                    img_path = save_image(fs, 'products')
                except Exception as e:
                    # 确保不会因为一张图导致整个商品保存失败
                    current_app.logger.warning(f"[GALLERY] 图片保存失败: {e}")
                    continue

                db.session.add(ProductImage(
                    product_id=p.id,
                    image_path=img_path
                ))

            db.session.commit()
            flash('保存成功', 'success')
            return redirect(url_for('admin.product_list'))

        except Exception as e:
            db.session.rollback()
            current_app.logger.exception("Create product failed")
            flash(f'保存失败：{e}', 'danger')
            abort(500)
    else:
        if request.method == 'POST':
            current_app.logger.warning("Product form invalid: %s", form.errors)
            # 返回 400 让你一眼能看出来不是成功
            return render_template('admin/product_form.html', form=form), 400
    return render_template('admin/product_form.html', form=form)


@bp.route('/products/<int:pid>/edit', methods=['GET', 'POST'])
@login_required
def product_edit(pid):
    p = Product.query.get_or_404(pid)
    form = ProductForm(obj=p)
    form.category_id.choices = [(c.id, c.name) for c in Category.query.order_by(Category.sort_order)]
    i18n_en = ProductI18n.query.filter_by(product_id=p.id, lang='en').first()

    if request.method == 'GET':
        form.price_amount.data = (Decimal(p.price) / 100) if p.price is not None else None
        form.currency.data = p.currency or 'CNY'
        form.has_certificate.data = '1' if p.has_certificate else '0'
        form.can_direct_pay.data = '1' if p.can_direct_pay else '0'
        if i18n_en:
            form.title_en.data = i18n_en.title
            form.subtitle_en.data = i18n_en.subtitle
            form.description_en.data = i18n_en.description
            form.material_en.data = i18n_en.material
        return render_template("admin/product_form.html", form=form, p=p)

    if form.validate_on_submit():
        # 覆盖主语言字段
        p.title = form.title.data
        p.subtitle = form.subtitle.data
        p.description = form.description.data
        p.currency = form.currency.data
        p.material = form.material.data
        p.size_value = float(form.size_value.data) if form.size_value.data is not None else None
        p.size_length = float(form.size_length.data) if form.size_length.data is not None else None
        p.size_width = float(form.size_width.data) if form.size_width.data is not None else None
        p.size_height = float(form.size_height.data) if form.size_height.data is not None else None

        p.size_unit = form.size_unit.data
        p.weight_value = float(form.weight_value.data) if form.weight_value.data is not None else None
        p.weight_unit = form.weight_unit.data
        p.stock = form.stock.data
        p.has_certificate = (form.has_certificate.data == '1')
        p.can_direct_pay = (form.can_direct_pay.data == '1')
        p.sku = form.sku.data
        p.category_id = form.category_id.data
        p.min_order_qty = form.min_order_qty.data or 1
        p.is_published = form.is_published.data
        if form.price_amount.data is not None:
            # 在 product_edit 的 POST 分支里：
            val = form.price_amount.data  # 可能是 None / '' / str / int/float / Decimal

            amount_cents = None
            if val not in (None, ''):
                try:
                    amount = val if isinstance(val, Decimal) else Decimal(str(val))
                    # 存“分”避免浮点误差；四舍五入
                    amount_cents = int((amount * 100).to_integral_value(rounding=ROUND_HALF_UP))
                except (InvalidOperation, ValueError) as err:
                    flash("价格格式不合法，请输入数字（例如 100 或 99.90）", "danger")
                    return render_template('admin/product_form.html', form=form, p=p), 400

            p.price = amount_cents  # None 表示无价格
            p.currency = (form.currency.data or "CNY").upper()

        if form.cover_image.data:
            p.cover_image = save_image(form.cover_image.data, 'products')
        # 英文翻译保存
        if any([form.title_en.data, form.subtitle_en.data, form.description_en.data]):
            if i18n_en:
                i18n_en.title = form.title_en.data or None
                i18n_en.subtitle = form.subtitle_en.data or None
                i18n_en.description = form.description_en.data or None
                i18n_en.material = form.material_en.data or None
            else:
                db.session.add(ProductI18n(product_id=p.id, lang='en',
                               title=form.title_en.data or None,
                               subtitle=form.subtitle_en.data or None,
                               description=form.description_en.data or None,
                               material=form.material_en.data or None))
        else:
            if i18n_en:
                db.session.delete(i18n_en)

        gallery_files = request.files.getlist('gallery')

        for fs in gallery_files:
            if fs and fs.filename:
                path = save_image(fs, 'products')
                db.session.add(ProductImage(product_id=p.id, image_path=path))
        db.session.commit()
        return redirect(url_for('admin.product_list'))

    return render_template('admin/product_form.html', form=form, p=p)


@bp.route('/products/<int:pid>/delete', methods=['POST'])
@login_required
def product_delete(pid):
    p = Product.query.get_or_404(pid)
    BuyClick.query.filter_by(product_id=pid).delete()
    db.session.delete(p)
    db.session.commit()
    return redirect(url_for('admin.product_list'))


@bp.post('/products/<int:pid>/images/<int:iid>/delete')
@login_required
def product_image_delete(pid, iid):
    img = ProductImage.query.filter_by(id=iid, product_id=pid).first_or_404()
    db.session.delete(img)
    db.session.commit()
    return redirect(url_for('admin.product_edit', pid=pid))


@bp.post('/products/<int:pid>/cover/delete')
@login_required
def product_cover_delete(pid):
    p = Product.query.get_or_404(pid)
    p.cover_image = None
    db.session.commit()
    return redirect(url_for('admin.product_edit', pid=pid))




# 轮播图管理（简版）
@bp.route('/banners')
@login_required
def banner_list():
    items = Banner.query.order_by(Banner.sort_order).all()
    return render_template('admin/banner_list.html', items=items)


@bp.route('/banners/new', methods=['POST'])
@login_required
def banner_new():
    img = request.files.get('image')
    title = request.form.get('title')
    link_url = request.form.get('link_url')
    sort_order = int(request.form.get('sort_order') or 0)
    path = save_image(img, 'banners') if img else None
    b = Banner(title=title, link_url=link_url, sort_order=sort_order, image_path=path)
    db.session.add(b)
    db.session.commit()
    return redirect(url_for('admin.banner_list'))


@bp.route('/customers')
@login_required
def customers():
    q = (request.args.get('q') or '').strip()
    page = request.args.get('page', type=int, default=1)
    per_page = request.args.get('per_page', type=int, default=20)

    query = CustomerUser.query
    if q:
        if q.isdigit():
            query = query.filter(or_(CustomerUser.id==int(q),
                                     CustomerUser.username.ilike(f'%{q}%')))
        else:
            query = query.filter(CustomerUser.username.ilike(f'%{q}%'))

    pagination = query.order_by(CustomerUser.created_at.desc()).paginate(page=page, per_page=per_page, error_out=False)
    return render_template('admin/customers.html', q=q, pagination=pagination, items=pagination.items)


def _bucket_and_label(bucket: str):
    """返回 (bucket_expr, label_expr, label_format) 供 SQLAlchemy 使用"""
    if bucket == 'week':
        expr = func.YEARWEEK(CustomerUser.created_at, 3)
        label = 'yw'
        return expr, label, 'week'
    elif bucket == 'month':
        expr = func.DATE_FORMAT(CustomerUser.created_at, '%Y-%m')
        label = 'ym'
        return expr, label, 'month'
    else:
        expr = func.DATE(CustomerUser.created_at)
        label = 'd'
        return expr, label, 'day'


@bp.route('/stats')
@login_required
def stats():
    # 参数
    bucket = request.args.get('bucket', 'day')  # day|week|month
    days = request.args.get('days', type=int, default=30)  # 统计窗口
    end = datetime.utcnow()
    start = end - timedelta(days=days)

    # --- 新用户 ---
    expr_u, label_u, _ = _bucket_and_label(bucket)
    new_users = (db.session.query(expr_u.label('bucket'), func.count('*').label('cnt'))
                 .select_from(CustomerUser)
                 .filter(CustomerUser.created_at.between(start, end))
                 .group_by('bucket')
                 .order_by('bucket')
                 ).all()

    # --- 已支付订单 ---
    if bucket == 'week':
        expr_o = func.YEARWEEK(Order.created_at, 3)
    elif bucket == 'month':
        expr_o = func.DATE_FORMAT(Order.created_at, '%Y-%m')
    else:
        expr_o = func.DATE(Order.created_at)

    orders_paid = (db.session.query(expr_o.label('bucket'), func.count('*').label('cnt'))
                   .select_from(Order)
                   .filter(Order.status=='paid', Order.created_at.between(start, end))
                   .group_by('bucket').order_by('bucket')).all()

    # --- 点击购买（全部渠道 & 分渠道） ---
    if bucket == 'week':
        expr_c = func.YEARWEEK(BuyClick.created_at, 3)
    elif bucket == 'month':
        expr_c = func.DATE_FORMAT(BuyClick.created_at, '%Y-%m')
    else:
        expr_c = func.DATE(BuyClick.created_at)

    clicks_total = (db.session.query(expr_c.label('bucket'), func.count('*').label('cnt'))
                    .select_from(BuyClick)
                    .filter(BuyClick.created_at.between(start, end))
                    .group_by('bucket').order_by('bucket')).all()

    clicks_by_channel = (db.session.query(expr_c.label('bucket'), BuyClick.channel,
                                          func.count('*').label('cnt'))
                         .select_from(BuyClick)
                         .filter(BuyClick.created_at.between(start, end))
                         .group_by('bucket', BuyClick.channel)
                         .order_by('bucket', BuyClick.channel)).all()

    # 把结果转为简单字典，模板好渲染
    def to_map(rows):
        return {str(r[0]): int(r[1]) for r in rows}
    def to_map_channel(rows):
        out = {}
        for b, ch, cnt in rows:
            out.setdefault(str(b), {})[ch] = int(cnt)
        return out

    ctx = {
        'bucket': bucket, 'days': days,
        'window_start': start, 'window_end': end,
        'new_users': to_map(new_users),
        'orders_paid': to_map(orders_paid),
        'clicks_total': to_map(clicks_total),
        'clicks_by_channel': to_map_channel(clicks_by_channel),
    }
    return render_template('admin/stats.html', **ctx)


@bp.route('/<int:order_id>', methods=['POST'])
@login_required
def mark_shipped(order_id):
    require_admin()
    data = request.json or {}
    carrier = data.get('carrier', '').strip()
    tracking_no = data.get('tracking_no', '').strip()
    if not carrier or not tracking_no:
        return jsonify({'ok': False, 'error': 'carrier and tracking_no required'}), 400

    order = Order.query.get_or_404(order_id)
    shipment = Shipment(order_id=order.id, carrier=carrier, tracking_no=tracking_no)
    order.fulfillment_status = 'shipped'
    order.shipped_at = datetime.utcnow()

    db.session.add(shipment)
    db.session.commit()
    return jsonify({'ok': True})
